package org.naxx.security.parser.funcdef;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.naxx.security.datatype.DataType;
import org.naxx.security.datatype.DataTypeResolver;
import org.naxx.security.function.FunctionType;
import org.naxx.security.parser.AbstractXmlParser;
import org.naxx.security.parser.util.QNames;

public class FunctionTypeDefinitionParser extends AbstractXmlParser<Set<FunctionType>> {
	
//	private static final Logger logger = Logger
//			.getLogger(FunctionTypeDefinitionParser.class);
	
	private DataTypeResolver dataTypeResolver;
	
	public void setDataTypeResolver(DataTypeResolver dataTypeResolver) {
		this.dataTypeResolver = dataTypeResolver;
	}

	/**
	 * Precondition: cursor points just before Functions element.<br>
	 * Postcondition: cursor points to closing Functions element<br>
	 */
	public Set<FunctionType> parse(XMLEventReader reader) throws Exception {
		Set<FunctionType> result = new HashSet<FunctionType>();
		this.nextTag(reader);
		this.skipCharactersComments(reader);
		while(reader.peek().isStartElement() 
				&& reader.peek().asStartElement().getName().getLocalPart().equals("Function")) {
			result.add(this.parseOneFunctionType(reader));
			this.skipCharactersComments(reader);
		}
		this.nextTag(reader);
		return result;
	}

	/**
	 * Precondition: Cursor points to element just before opening Function element.<br>
	 * Postcondition: Cursor points to closing Function element.
	 */
	FunctionType parseOneFunctionType(XMLEventReader reader) throws Exception {
//		logger.info("Parsing function type fragment");
		XMLEvent event = reader.nextTag();
		StartElement start = event.asStartElement();
		Attribute idAttr = start.getAttributeByName(QNames.ID_ATTRIBUTE);
//		Attribute isGenericAttr = start.getAttributeByName(QNames.GENERIC_ATTRIBUTE);
		
		event = reader.nextTag(); // now at Uri element
		event = reader.nextEvent(); // now at characters
		String uri = event.asCharacters().getData();
		this.skipCharactersComments(reader);
		event = reader.nextTag(); // now at closing Uri
		
		event = reader.nextTag();
		event = reader.nextEvent();// now at characters
		String name = event.asCharacters().getData();
		this.skipCharactersComments(reader);
		event = reader.nextTag(); // now at closing name
		
		event = reader.nextTag(); // now at ReturnDataType
		String returnDataType = event.asStartElement().getAttributeByName(
				QNames.DATA_TYPE_ID_ATTRIBUTE).getValue();
		
		event = reader.nextTag(); // closing 
		
		event = reader.nextTag();
		int numberOfParameters = Integer.valueOf(event.asStartElement()
				.getAttributeByName(QNames.NUMBER_OF_PARAMETERS_ATTRIBUTE).getValue());
		
		FunctionType functionType = new FunctionType();
		functionType.setId(Integer.valueOf(idAttr.getValue()));
		functionType.setName(name);
		functionType.setUri(uri);
		functionType.setNumberOfParameters(numberOfParameters);
		functionType.setReturnDataType(this.dataTypeResolver.resolveDataType(Integer.valueOf(returnDataType)));
		List<DataType> params = new ArrayList<DataType>();
		event = this.nextTag(reader); // now at param
		while(true) {
			start = event.asStartElement();
			Attribute paramDataTypeAttr = start
					.getAttributeByName(QNames.DATA_TYPE_ID_ATTRIBUTE);
			DataType paramType = this.dataTypeResolver.resolveDataType(Integer.valueOf(paramDataTypeAttr.getValue()));
			params.add(paramType);
			event = this.nextTag(reader); //close opening
			event = this.nextTag(reader); // next Param or end of Params
			if(event.isEndElement() 
					&& "Parameters".equals(event.asEndElement().getName().getLocalPart())) {
				break;
			}
		}
		functionType.setParameters(params.toArray(new DataType[params.size()]));
		event = this.nextTag(reader);
		StringBuilder buffer = new StringBuilder();
		this.getCharacters(reader, buffer);
		functionType.setXriptlet(buffer.toString().trim());
		event = reader.nextTag();
		
		return functionType;
	}
}
